/*
Kakine
ActionScript Framework
Copyright © 2011, PUNY, LLC.

This file is part of KAKINE.

KAKINE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

KAKINE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KAKINE.  If not, see <http://www.gnu.org/licenses/>.
*/ 

package com.puny.mvc.views
{
	import com.puny.mvc.models.EntityModels;
	import com.puny.mvc.models.Model;
	import com.puny.utils.ObjectFinder;
	
	import flash.display.FrameLabel;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	
	/**
	 * $Revision: 723 $ <br/>
	 * This defines abstract class of views including button features for this MVC archtecture. An Asset for a button in this framework should be an exported <code>MovieClip</code> instead of <code>SimpleButton</code>
	 * The <code>MovieClip</code> must have at least <code>out</code>,<code>over</code> and <code>down</code> states which should be time line frame labels.
	 * @example Here is a sample code for a view class which uses an external swf.
	 * <listing version="3.0">
	 * public calss SampleView extends View {
	 *		public function SampleView() {
	 * 			super("assets/my_assets.swf");
	 *		}
	 * 		
	 * 		override protected function setup():void {
	 * 			addButtonListeners(getMovieClip("MyButton"), buttonHandler);
	 * 		}
	 * 
	 * 		private function buttonHandler(event:Evnet):void {
	 * 			trace("Button is clicked!");
	 * 		}
	 * }
	 * </listing>
	 * @example Here is another sample code for a view class which uses an embedded swf.
	 * <listing version="3.0">
	 * public class EmbededView extends CommonView {
	 *		[Embed(source="assets/my_assets.swf")]
	 *		private var swf:Class;
	 *
	 *		public function EmbededView() {
	 *			super(new swf());
	 *		}
	 * 
	 * 		override protected function setup():void {
	 * 			addButtonListeners(getMovieClip("MyButton"), buttonHandler);
	 * 		}
	 * 
	 * 		private function buttonHandler(event:Evnet):void {
	 * 			trace("Button is clicked!");
	 * 		}
	 * }
	 * </listing>
	 * <listing version="3.0">
	 * public class EmbededView extends CommonView {
	 *		[Embed(source="assets/my_assets.swf")]
	 *		private var swf:Class;
	 *
	 *		public function EmbededView() {
	 *			super(swf);
	 *		}
	 * 
	 * 		override protected function setup():void {
	 * 			addButtonListeners(getMovieClip("MyButton"), buttonHandler);
	 * 		}
	 * 
	 * 		private function buttonHandler(event:Evnet):void {
	 * 			trace("Button is clicked!");
	 * 		}
	 * }
	 * </listing>
	 * 
	 * This is a chart that shows you the method order to be invoked.
	 * Each method can be overrode when you need.
	 * <br/>
	 * <br/>
	 * loaded assets;<code>setup()</code>
	 * <br/>
	 * <br/>
	 * loaded assets &amp; added on stage;<code>beforeReady()</code>;
	 * <code>READY</code> event;
	 * <code>afterReady()</code>;
	 * <code>beforeOpen()</code>;
	 * <code>procedureOpen()</code>;
	 * <code>OPENED</code> event;
	 * <code>afterOpen()</code>;
	 * <code>beforeMainFrame()</code>;
	 * <code>procedureMainFrame()</code>;
	 * <code>afterMainFrame()</code>
	 * <br/>
	 * <br/>
	 * called <code>complete()</code> method; 
	 * <code>beforeClose()</code>;
	 * <code>procedureClose()</code>;
	 * <code>afterClose()</code>;
	 * <code>COMPLETED</code> event;
	 * <code>beforeChildrenRemoved()</code>;
	 * <code>afterChildrenRemoved()</code>
	 * <br/>
	 */
	public class ButtonView extends View
	{
		private static const _initialized:Boolean = staticInitializer();
		/**
		 * A frame label for <code>MouseEvent.ROLL_OUT</code> image. 
		 * The label name is case-sensitive.
		 */
		public static const OUT:String = "out";

		/**
		 * A frame label for <code>MouseEvent.ROLL_OVER</code> image. 
		 * The label name is case-sensitive.
		 */
		public static const OVER:String = "over";

		/**
		 * A frame label for <code>MouseEvent.CLICK</code> image. 
		 * The label name is case-sensitive.
		 */
		public static const CLICKED:String = "clicked";
		
		/**
		 * A frame label for <code>MouseEvent.MOUSE_DOWN</code> image. 
		 * The label name is case-sensitive.
		 */
		public static const DOWN:String = "down";
		
		/**
		 * A frame label for <code>MouseEvent.MOUSE_UP</code> image. 
		 * The label name is case-sensitive.
		 */
		public static const UP:String = "up";
		/**
		 * A frame label for an animation when <code>MouseEvent.ROLL_OVER</code> dispatches.
		 * After this animation, the current label stops <code>OVER</code> if it exists.
		 * Otherwise, it goes back to the frame before the animation.
		 * The label name is case-sensitive.
		 */
		public static const OUT_2_OVER:String = "out2over";
		/**
		 * A frame label for an animation when <code>MouseEvent.ROLL_OUT</code> dispatches.
		 * After this animation, the current label stops <code>OUT</code> if it exists.
		 * Otherwise, it goes back to the frame before the animation.
		 * The label name is case-sensitive.
		 */
		public static const OVER_2_OUT:String = "over2out";
		/**
		 * A frame label for an animation when <code>MouseEvent.DOWN</code> dispatches.
		 * After this animation, the current label stops <code>DOWN</code> if it exists.
		 * Otherwise, it goes back to the frame before the animation.
		 * The label name is case-sensitive.
		 */
		public static const OVER_2_DOWN:String = "over2down";
		/**
		 * A frame label for an animation when <code>MouseEvent.UP</code> dispatches.
		 * After this animation, the current label stops <code>OVER</code> if it exists.
		 * Otherwise, it goes back to the frame before the animation.
		 * The label name is case-sensitive.
		 */
		public static const DOWN_2_OVER:String = "down2over";
		/**
		 * A frame label for an animation when <code>MouseEvent.CLICK</code> dispatches.
		 * After this animation, the current label stops <code>CLICKED</code> if it exists.
		 * Otherwise, it goes back to the frame before the animation.
		 * The label name is case-sensitive.
		 */
		public static const CLICK:String = "over2clicked";
		
		private var _functions:Object;
		private const _registeredButtons:Array = new Array();

		public function ButtonView(src:Object=null)
		{
			super(src);
		}
		
		private static function staticInitializer():Boolean {
			initializeState(MouseEvent.ROLL_OUT, OVER_2_OUT, OUT);
			initializeState(MouseEvent.ROLL_OVER, OUT_2_OVER, OVER);
			initializeState(MouseEvent.MOUSE_DOWN, OVER_2_DOWN, DOWN);
			initializeState(MouseEvent.MOUSE_UP, DOWN_2_OVER, UP);
			initializeState(MouseEvent.CLICK, CLICK, CLICKED);
			return true;
		}

		private static function initializeState(type:String,label:String,goal:String):void {
			var state:Model = EntityModels.getInstance(type);
			state.label = label;
			state.goal = goal;
		}
		


//		/**
//		 * Load an exported asset form the loaded swf file as a MovieClip
//		 * 
//		 * @param target Will be <code>event.target</code> in <code>loadChildren</code> method
//		 * @param name Will be an exported name as a class in the loaded swf file.
//		 * @param add Will be added in this view class immediately if it's true.
//		 * @param cache <code>cacheAsBitmap</code> of this MovieClip is true if it's true.
//		 * @return Instance of a MovieClip
//		 */
//		override protected function getMovieClip(name:String,add:Boolean=true,cache:Boolean=true):MovieClip {
//			var mc:MovieClip = super.getMovieClip(name,add,cache);
//			analyzeFrame(mc);
//			return mc;
//		}

		/**
		 * 
		 * @param event
		 * @return 
		 * @private
		 */
		protected function buttonEventHandler(event:MouseEvent):MovieClip {
			var btn:MovieClip = event.target as MovieClip;
			var type:String = EntityModels.getInstance(event.type).goal;
			if (btn[type]) {
				trace(type+": "+btn.name);
				btn.gotoAndStop(type);
			} else {
				btn.gotoAndStop("over");
			}
			return btn;
		}
		
		/**
		 * This will be invoked when a MouseEvent.CLICK is dispatched
		 * This makes the current frame of the target MovieClip "CLICKED" with <code>gotoAndStop</code> method
		 */
		protected function clickeHandler(event:MouseEvent):void {
			var btn:MovieClip = buttonEventHandler(event);
			if (_functions!=null && _functions[btn.name]!= null) {
				_functions[btn.name](event);
			}
		}
		
		/**
		 * This makes a MovieClip a button like the following.
		 * <ul>
		 *   <li><code>useHandCursor = true</code></li>
		 *   <li><code>buttonMode = true</code></li>
		 *   <li><code>mouseChildren = false</code></li>
		 *   <li><code>ROLL_OUT</code>,<code>ROLL_OVER</code>,<code>MOUSE_CLICK</code>,<code>MOUSE_UP</code>, and <code>MOUSE_DOWN</code> listeneres are added</li>
		 * </ul>
		 * "OUT" is the default frame label. If this MovieClip doesn't have "OUT" label, frame 0 is the default. Default event handlers for <code>ROLL_OVER</code> and <code>MOUSE_UP</code> are the same.
		 * 
		 * @param btn a MovieClip that should act as a Button
		 * @param func a function invoked when a user click this button.
		 * @param simple if <code>true</code>, the only <code>MouseEvent.CLICK</code> will be handled.
		 * @param cover if <code>true</code>, the given MovieClip is covered with invisible rectangle.
		 */
		protected function addButtonListeners(btn:MovieClip,func:Function=null ,simple:Boolean=false, sw:Boolean=false):void {
			analyzeFrame(btn);
			btn.useHandCursor = true;
			btn.buttonMode = true;
			btn.mouseChildren = false;
			if (sw) {
				cover(btn);
			}
			if (!simple) {
				btn.addEventListener(MouseEvent.ROLL_OUT, buttonEventHandler, false, 0, true);
				btn.addEventListener(MouseEvent.ROLL_OVER, buttonEventHandler, false, 0, true);
				btn.addEventListener(MouseEvent.MOUSE_DOWN, buttonEventHandler, false, 0, true);
				btn.addEventListener(MouseEvent.MOUSE_UP, buttonEventHandler, false, 0, true);
			}
			btn.addEventListener(MouseEvent.CLICK, clickeHandler, false, 0, true);

			trace("Function for", btn.name, " is added");
			if (_functions == null) {
				_functions = new Object();
			}
			_functions[btn.name] = func;
			if (btn[OUT]) {
				btn.gotoAndStop(OUT);
			} else {
				btn.gotoAndStop(0);
			}
		}
		
		/**
		 *　Register buttons.
		 * When a button is registered, an Event will be dipstached. Its event type should be the class name of the button.
		 * @param buttons
		 * 
		 */		
		protected function registerButtons(...buttons):void {
			for each(var btn:MovieClip in buttons) {
				addButtonListeners(btn, dispatchHandler);
				_registeredButtons.push(btn);
			}
		}
		
		protected function dispatchHandler(event:Event):void {
			var event:Event = new Event(ObjectFinder.eliminateObjectName(event.currentTarget.toString()));
			dispatchEvent(event);
		}
		
		private function unregisterButtons():void {
			var btn:MovieClip
			while(_registeredButtons.length > 0) {
				btn = _registeredButtons.shift();
				removeButtonListeners(btn);
			}
		}
		
		private function cover(mc:MovieClip):void {
			var bound:Rectangle = mc.getBounds(mc);
			var wrap:Shape = new Shape();
			wrap.graphics.beginFill(0xFFFFFF,0);
			wrap.graphics.drawRect(bound.x,bound.y,mc.width,mc.height);
			wrap.graphics.endFill();
			mc.addChild(wrap);
		}
		
		private function analyzeFrame(mc:MovieClip):void {
			trace("MovieClip:",mc.name);
			for each(var label:FrameLabel in mc.currentLabels) {
				trace(" label:", label.name, "at", label.frame);
				mc[label.name] = true;
			}
		}
		
		/**
		 * This removes button effects from a given MovieClip.
		 * @param btn MovieClip which is acting as a button
		 * @see com.puny.mvc.views.View#addButtonListeners
		 */
		protected function removeButtonListeners(btn:MovieClip):void {
			btn.removeEventListener(MouseEvent.ROLL_OUT, buttonEventHandler);
			btn.removeEventListener(MouseEvent.ROLL_OVER, buttonEventHandler);
			btn.removeEventListener(MouseEvent.MOUSE_DOWN, buttonEventHandler);
			btn.removeEventListener(MouseEvent.MOUSE_UP, buttonEventHandler);
			btn.removeEventListener(MouseEvent.CLICK, clickeHandler);
			btn.useHandCursor = false;
			btn.buttonMode = false;
			btn.mouseChildren = true;
		}
		
		/**
		 * @private 
		 * 
		 */
		override public function afterClose():void {
			super.afterClose();
			unregisterButtons();
			for(var key:String in _functions) {
//				trace("Function for", key, " is removed");
				_functions[key]=null;
			}
			_functions = null;
		} 
	}
}